package com.runa.monitor.platinterface.platService;

import com.alibaba.fastjson.JSON;
import com.runa.monitor.comm.dto.Archive;
import com.runa.monitor.comm.dto.Node;
import com.runa.monitor.comm.dto.Valve;
import com.runa.monitor.comm.entity.CommNetEquipment;
import com.runa.monitor.comm.entity.CommNetService;
import com.runa.monitor.platinterface.backwork.function.TaskFunction;
import com.runa.monitor.platinterface.backwork.impl.*;
import com.runa.monitor.platinterface.netservice.equipment.*;
import com.runa.monitor.platinterface.netservice.equipment.debao.DeBao2Driver;
import com.runa.monitor.platinterface.netservice.equipment.dtu.JunPeiHdDtuDriver;
import com.runa.monitor.platinterface.netservice.equipment.furuisi.FuRuiSiClientDriver;
import com.runa.monitor.platinterface.netservice.server.ListenerServer;
import com.runa.persistence.config.CommConfig;
import com.runa.persistence.entity.apportion.ApportionValveControlParam;
import com.runa.persistence.util.LogUtil;
import com.runa.protocol.enums.ModbusType;
import com.runa.rccprotocol.dto.equdata.EquData;
import com.runa.rccprotocol.dto.param.MeterReadingCycle;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Slf4j
@Service
public class NetEquService implements ApplicationRunner {

    @Value("${scheduler.size.commandPost:1}")
    private int THREAD_SIZE;

    @Resource
    private RepositoryService repositoryService;

    @Override
    public void run(ApplicationArguments args) throws Exception {
        Executors
                .newSingleThreadScheduledExecutor()
                .scheduleAtFixedRate(
                        () -> netEquPool.forEach((serial, netEqu) -> {
                            if (netEqu.online()) {
                                if (netEqu.heartStop()) {
                                    netEqu.close();
                                    log.info("心跳超时，关闭 : {}", serial);
                                }
                            }
                        }), 10, 10, TimeUnit.SECONDS);
        for (int i = 0; i < THREAD_SIZE; i++) {
            int I = i;
            new Thread(() -> {
                while (true) {
                    int index = 0;
                    for (NetEqu<String> netEqu : netEquPool.values()) {
                        if (index%THREAD_SIZE == I) {
                            netEqu.requestPost();
                        }
                        index++;
                    }
                    try {
                        Thread.sleep(1);
                    } catch (InterruptedException ignored) {}
                }
            },"request-post-scheduler-"+i).start();
        }
    }

    private String localIp;

    @Value("${dispatch.grpc.local.ip:}")
    public void setLocalIp(String localIp) {
        this.localIp = localIp;
    }

    private final Map<Object, NetEqu<String>> netEquPool = new ConcurrentHashMap<>();

    public void setInstance(Object serial, NetEqu<String> netEqu) {
        netEquPool.put(serial, netEqu);
    }

    //设备离线时清除
    public void removeInstance(Object serial) {
        if (serial != null)
            netEquPool.remove(serial);
    }

    public NetEqu<String> getNetEquByCommNet(CommNetEquipment commNetEquipment) {
        String serial = commNetEquipment.getSerial();
        NetEqu<String> netEqu = netEquPool.get(serial);
        if (null != netEqu && netEqu.notChange(commNetEquipment)) {
            netEqu.timeout(commNetEquipment.getTimeout());
            return netEqu;
        }
        netEqu = getNetEqu(commNetEquipment);
        if (null != netEqu) {
            netEqu.init(commNetEquipment);
            setInstance(serial, netEqu);
        }
        return netEqu;
    }

    /**
     * 获取NetEqu
     */
    public NetEqu<String> getNetEquBySerial(String serial) {
        NetEqu<String> netEqu = getNetEquBySerialCache(serial);
        if (netEqu != null) return netEqu;
        CommNetEquipment commNetEquipment = repositoryService.getCommNetEquipmentByNetSerial(serial);
        if (commNetEquipment == null) {
            removeInstance(serial);
            return null;
        }
        return getNetEquByCommNet(commNetEquipment);
    }

    public NetEqu<String> getNetEquBySerialCache(String serial) {
        if (null == serial) {
            return null;
        }
        return netEquPool.get(serial);
    }

    private NetEqu<String> eeee = null;

    public NetEqu<String> getEeee() {
        if (eeee == null) {
            CommNetEquipment equipment = new CommNetEquipment();
            CommNetService netService = new CommNetService();
            netService.setProtocol("EEEE");
            eeee = getNetEqu(equipment);
        }
        return eeee;
    }

    public int getServiceOnlineSize(Integer service) {
        return (int) netEquPool.values().stream().filter(equ ->
                null != equ.getService() && equ.getService().equals(service) && equ.online()).count();
    }

    private final Map<String, Boolean> aiStormOnOff = new ConcurrentHashMap<>();

    public void aiStormOnline(String serial) {
        aiStormOnOff.put(serial, true);
    }

    public void aiStormOffline(String serial) {
        aiStormOnOff.put(serial, false);
    }

    public boolean getAiStormOnOff(String serial) {
        return aiStormOnOff.get(serial) != null && aiStormOnOff.get(serial);
    }

    private NetEqu<String> getNetEqu(CommNetEquipment netEqu) {
        if ("3.0网络设备".equals(netEqu.getModel())) {
            return new ThreePointZeroDriver(netEqu.getSerial());
        }
        CommNetService netService = netEqu.getCommNetService();
        if (netService == null) {
            log.error("网络设备协议为空：" + JSON.toJSONString(netEqu));
            return null;
        }
        Integer service = netService.getId();
        switch (netService.getProtocol()) {
            case "RUNA_FMU":
                return new Concentrator(netEqu.getSerial(), netEqu.getTimeout(), netEqu.getElecSerial()).setService(service);
            case "OTHER_RUNA_FMU":
                return new RunaUnifyDriver(netEqu.getSerial(), netEqu.getTimeout(), netEqu.getElecSerial(), netEqu.getModel()).setService(service);
            case "GD_FMU":
                return new GdDriver(netEqu.getSerial(), netEqu.getTimeout()).setService(service);
            case "QY_FMU":
                return new QyDriver(netEqu.getSerial(), netEqu.getTimeout()).setService(service);
            case "HWS_DEVICE":
                switch (netEqu.getModel()) {
                    case "HIWITS":
                        return new HiwitsDriver(netEqu.getSerial(), netEqu.getTimeout()).setService(service);
                    case "":
                        break;
                }
                break;
            case "RUNA_LORA":
                switch (netEqu.getModel()) {
                    case "RU-CH-03":
                        return new RunaLoraDriver(netEqu.getSerial(), netEqu.getTimeout()).setService(service);
                    case "":
                        break;
                }
                break;
//            case "RUNA_EQU":
            case "RUNA_NATIVE":
                return new RunaNativeDriver(netEqu.getSerial(), netEqu.getTimeout()).setService(service);
            case "En_Meter":
                return new EnDriver(netEqu.getSerial(), netEqu.getTimeout()).setService(service);
            case "DFS_CLR"://潍坊丹弗斯的采集器
                switch (netEqu.getModel()) {
                    case "DANFOSS":
                        return new NovoconDriver(netEqu.getSerial(), netEqu.getTimeout(), netEqu.getModel()).setService(service);
                    case "":
                        break;
                }
                break;
            case "HZ_FMU":
                switch (netEqu.getModel()) {
                    case "HZ_4103":
                        return new AutoUpwardDriver(netEqu.getSerial(), netEqu.getModel(), new ReportTaskFunction(netEqu.getId())).setService(service);
                    case "":
                        break;
                }
                break;
            case "FK_NBIot":
            case "JN_TMP"://厂家：艾科瑞，型号：JN_TMP_DE
            case "SH_TMP"://乌兰察布京能面板
            case "EEEE": //内部虚拟主动上报类网络设备
            case "HZ_TMP_FMU":
                return new AutoUpwardDriver(netEqu.getSerial(), netEqu.getModel(), new ReportTaskFunction(netEqu.getId())).setService(service);
            case "JJN_FMU":
                return new Jjn8124Driver(netEqu.getSerial(), netEqu.getTimeout()).setService(service);
            case "DAOHENG_FMU":
                switch (netEqu.getModel()) {
                    case "CJ-MBUS-4G-001":
                        return new DaoHengDriver(netEqu.getSerial(), netEqu.getTimeout(), netEqu.getElecSerial()).setService(service);
                    case "":
                        break;
                }
                break;
            case "TIANGANG_FMU":
            case "TIANGANG_UDP_FMU":
                switch (netEqu.getModel()) {
                    case "CR62-MR":
                    case "CR40":
                        return new TianGangDriver(netEqu.getSerial(), netEqu.getTimeout(), netEqu.getElecSerial(), netEqu.getModel()).setService(service);
                    case "":
                        break;
                }
                break;
            case "HUANXIANG_FMU":
                switch (netEqu.getModel()) {
                    case "RLB-M-Bus-360":
                        return new HuanXiangDriver(localIp, netEqu.getCommNetService().getPort().shortValue(), netEqu.getSerial(), netEqu.getTimeout()).setService(service);
                    case "":
                        break;
                }
                break;
            case "LJE_FMU":
                return new LanJiErDriver(netEqu.getSerial(), netEqu.getTimeout()).setService(service);
            case "LKB_FMU":
            case "LKB_DTU":
                return new LangKaBoDriver(netEqu.getId(), netEqu.getSerial(), netEqu.getModel(), netEqu.getTimeout()).setService(service);
            case "LC_FMU":
                return new LiChuangDriver(netEqu.getSerial(), netEqu.getTimeout()).setService(service);
            case "ELM_FMU":
                return new EnLeManDriver(netEqu.getSerial(), netEqu.getTimeout()).setService(service);
            case "DB_FMU":
                return new DeBaoDriver(netEqu.getSerial(), netEqu.getTimeout()).setService(service);
            case "DB_2_FMU":
                return new DeBao2Driver(netEqu.getSerial(), netEqu.getTimeout()).setService(service);
            case "HZ_HZ-1_FMU":
                return new HuiZhongOneDriver(netEqu.getSerial(), netEqu.getTimeout()).setService(service);
            case "JP_HD_DTU":
                return new JunPeiHdDtuDriver(netEqu).setService(service);
            case "SWP_DEVICE":
                switch (netEqu.getModel()) {
                    case "SWP-DTU":
                        return new SWPNativeDriver(netEqu.getSerial(), netEqu.getTimeout(), ListenerServer.getInstance("50", ModbusType.MODBUS_RTU, netEqu.getCommNetService().getPort())).setService(service);
                    case "":
                        break;
                }
                break;
            case "USR_G780":
                switch (netEqu.getModel()) {
                    case "NOVOCON":
                        return new NovoconDriver(netEqu.getSerial(), netEqu.getTimeout(), netEqu.getModel(), ListenerServer.getInstance("88", ModbusType.MODBUS_RTU, netEqu.getCommNetService().getPort())).setService(service);
                    case "":
                        break;
                }
                break;
            case "PUBLIC_BUILD":
                return new PublicBuildDriver(netEqu.getSerial(), netEqu.getTimeout(), ListenerServer.getInstance("50", ModbusType.MODBUS_RTU, netEqu.getCommNetService().getPort())).setService(service);
            case "HDB_FMU":
                return new HDBDriver(netEqu.getSerial()).setService(service);
            case "MQTT":
                switch (netEqu.getModel()) {
                    case "LDTU":
                        return new RunaLDtuDriver(netEqu.getSerial(), netEqu.getTimeout(),
                                new SubEquReadTaskFunction(null, netEqu.getId(), null, 0),
                                repositoryService.getCommValveByNetSerial(netEqu.getId())
                                        .stream().map(Valve::getValve).collect(Collectors.toList())).setService(service);
                    case "":
                        break;
                }
                break;
            case "AP_FMU":
                return new ApportionCollectorDriver(netEqu.getSerial(), netEqu.getTimeout()).setService(service);
            case "THTF_FMU":
                return new THTFDriver(netEqu.getSerial(), netEqu.getTimeout()).setService(service);
            case "TFNB_FMU":
                return new TFNBDriver(netEqu.getSerial(), netEqu.getTimeout(), CommConfig.getTfUrl(), CommConfig.getTfUsername(), CommConfig.getTfPassword()).setService(service);
            case "BLACK_ANT_FMU":
                return new BlackAntWebDriver(netEqu.getSerial(), netEqu.getTimeout(),
                        CommConfig.getBaUrl(), CommConfig.getBaUsername(), CommConfig.getBaPassword(),
                        CommConfig.getBadUrl(), CommConfig.getBadUsername(), CommConfig.getBadPassword()).setService(service);
            case "FRS_CLIENT":
                return FuRuiSiClientDriver.getInstance(netEqu);
            default:
        }
        return null;
    }

    public void readFmuNow(CommNetEquipment equipment, String taskNo,
                           String holdInfo, Integer priority) {
        NetEqu<String> netEqu = getNetEquByCommNet(equipment);
        if (netEqu instanceof Concentrator) {
            ((Concentrator) netEqu).readingNodeDataNow(new NetEquWriteTaskFunction(taskNo, holdInfo, equipment.getId(), priority));
        }
    }

    public void rccClearEquArchive(CommNetEquipment equipment, String taskNo,
                                   String holdInfo, Integer priority) {
        NetEqu<String> netEqu = getNetEquByCommNet(equipment);
        if (netEqu instanceof Concentrator) {
            netEqu.clearNodeEquArchive(new NetEquWriteTaskFunction(taskNo, holdInfo, equipment.getId(), priority));
        }
    }

    public void setNodeEquArchive(CommNetEquipment equipment, String taskNo,
                                  String holdInfo, Integer priority, List<Archive> archives) {
        NetEqu<String> netEqu = getNetEquByCommNet(equipment);
        netEqu.setNodeEquArchive(archives, new WriteArchiveTaskFunction(taskNo, holdInfo, priority, equipment.getId()));
    }

    public void readMeterReadingCycleParameter(CommNetEquipment equipment, String taskNo, String holdInfo, Integer priority) {
        NetEqu<String> netEqu = getNetEquByCommNet(equipment);
        if (netEqu instanceof Concentrator) {
            ((Concentrator) netEqu).readMeterReadingCycleParameter(new NetEquReadTaskFunction(taskNo, holdInfo, equipment.getId(), priority));
        }
    }

    public void readEquArchive(CommNetEquipment equipment, String taskNo,
                               String holdInfo, Integer priority, List<Integer> indexes) {
        NetEqu<String> netEqu = getNetEquByCommNet(equipment);
        netEqu.readNodeRecord(indexes, new ReadArchiveTaskFunction(taskNo, holdInfo, priority, equipment.getId()));
    }

    public void setMeterReadingCycleParameter(CommNetEquipment equipment, String taskNo,
                                              String holdInfo, Integer priority,
                                              MeterReadingCycle meterReadingCycle) {
        NetEqu<String> netEqu = getNetEquByCommNet(equipment);
        if (netEqu instanceof Concentrator) {
            ((Concentrator) netEqu).sentMeterReadingCycleParameter(meterReadingCycle,
                    new NetEquWriteTaskFunction(taskNo, holdInfo, equipment.getId(), priority));
        }
    }

    public void readNodeEquipment(CommNetEquipment equipment, List<Node> nodes, String taskNo,
                                  String holdInfo, EquData.EquDataType type, Integer priority) {
        LogUtil.info(equipment.getSerial(), "{} 类型: {} 设备：{}", taskNo, type, nodes.stream().map(Node::getSerial).collect(Collectors.joining(",")));
        NetEqu<String> netEqu = getNetEquByCommNet(equipment);
        netEqu.readNodeEqu(nodes, new SubEquReadTaskFunction(taskNo, equipment.getId(), holdInfo, priority), type);
    }

    public void readElectricityMeter(CommNetEquipment equipment) {
        NetEqu<String> netEqu = getNetEquByCommNet(equipment);
        if (netEqu instanceof Concentrator) {
            ((Concentrator) netEqu).readElectricityMeter();
        } else if (netEqu instanceof DaoHengDriver) {
            ((DaoHengDriver) netEqu).readElectricityMeter();
        } else if (netEqu instanceof LangKaBoDriver) {
            ((LangKaBoDriver) netEqu).readElectricityMeter();
        } else if (netEqu instanceof PublicBuildDriver) {
            ((PublicBuildDriver) netEqu).readElectricityMeter();
        }
    }

    public void buildValveParam(CommNetEquipment equipment, String taskNo, String holdInfo,
                                Integer priority, Node valve, Integer model) {
        NetEqu<String> netEqu = getNetEquByCommNet(equipment);
        netEqu.readPublicUnitRecord(valve, model, new SubEquReadTaskFunction(taskNo, equipment.getId(), holdInfo, priority));
    }

    public void valveOnOffControl(CommNetEquipment equipment, ApportionValveControlParam valveControl
            , String taskNo, String holdInfo, Integer priority) {
        NetEqu<String> netEqu = getNetEquByCommNet(equipment);
        if (netEqu instanceof ApportionCollectorDriver) {
            ((ApportionCollectorDriver)netEqu).valveOnOffControl(valveControl, new SubEquWriteTaskFunction(taskNo, holdInfo, priority));
        }
    }
    public void valveOnOffControl(CommNetEquipment equipment, String taskNo, String holdInfo,
                                  Integer priority, List<Node> valves, Integer open, boolean lock) {
        NetEqu<String> netEqu = getNetEquByCommNet(equipment);
        netEqu.valveOnOffControl(valves, lock, open, new SubEquWriteTaskFunction(taskNo, holdInfo, priority));
    }

    public void valveTemperatureControl(CommNetEquipment equipment, String taskNo, String holdInfo,
                                        Integer priority, List<Node> valves, float temp, boolean lock) {
        NetEqu<String> netEqu = getNetEquByCommNet(equipment);
        TaskFunction<Node, Object, Void> taskFunction = new SubEquWriteTaskFunction(taskNo, holdInfo, priority);
        if (netEqu instanceof Concentrator) {
            ((Concentrator) netEqu).valveTemperatureLock(valves, lock, temp, taskFunction);
        } else if (netEqu instanceof NovoconDriver) {
            ((NovoconDriver) netEqu).valveTemperatureLock(valves, lock, temp, taskFunction);
        } else if (netEqu instanceof AutoUpwardDriver) {
            ((AutoUpwardDriver) netEqu).valveTemperatureLock(valves, lock, temp, taskFunction);
        } else if (netEqu instanceof ApportionCollectorDriver) {
            ((ApportionCollectorDriver) netEqu).valveTemperatureLock(valves, lock, temp, taskFunction);
        } else if (netEqu instanceof RunaNativeDriver) {
            ((RunaNativeDriver) netEqu).valveTemperatureLock(valves, lock, temp, taskFunction);
        }
    }

    public void valveTemperatureLimit(CommNetEquipment equipment, String taskNo, String holdInfo,
                                      Integer priority, List<Node> valves, float tempUp, float tempDown) {
        NetEqu<String> netEqu = getNetEquByCommNet(equipment);
        TaskFunction<Node, Object, Void> taskFunction = new SubEquWriteTaskFunction(taskNo, holdInfo, priority);
        if (netEqu instanceof Concentrator) {
            ((Concentrator) netEqu).valveTemperatureLimit(valves, tempUp, tempDown, taskFunction);
        }  else if (netEqu instanceof NovoconDriver) {
            ((NovoconDriver) netEqu).valveTemperatureLimit(valves, tempUp, tempDown, taskFunction);
        } else if (netEqu instanceof RunaNativeDriver) {
            ((RunaNativeDriver) netEqu).valveTemperatureLimit(valves, tempUp, tempDown, taskFunction);
        }
    }

    public void sentPublicUnitRecord(CommNetEquipment equipment, String taskNo, String holdInfo,
                                     Integer priority, List<Node> valves, int model, Map<String, Object> param) {
        NetEqu<String> netEqu = getNetEquByCommNet(equipment);
        netEqu.sentPublicUnitRecord(valves, model, param, new SubEquWriteTaskFunction(taskNo, holdInfo, priority));
    }

    public void sentPublicUnitModel(CommNetEquipment equipment, String taskNo,
                                    String holdInfo, Integer priority, List<Node> valves, boolean model) {
        NetEqu<String> netEqu = getNetEquByCommNet(equipment);
        netEqu.sentPublicUnitModel(valves, model, new SubEquWriteTaskFunction(taskNo, holdInfo, priority));
    }

    public void temperatureCompensation(CommNetEquipment equipment, String taskNo,
                                        String holdInfo, Integer priority, List<Node> valves, float comp) {
        NetEqu<String> netEqu = getNetEquByCommNet(equipment);
        if (netEqu instanceof Concentrator) {
            ((Concentrator) netEqu).temperatureCompensation(valves, comp, new SubEquWriteTaskFunction(taskNo, holdInfo, priority));
        }
    }

}
